﻿
//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////

#include "CmdShowGNodeId.h"
#include "IEdge.h"
#include "IUiView.h"
#include "IElement.h"
#include "OwnerPtr.h"
#include "ICurve3d.h"
#include "Matrix4d.h"
#include "IDocument.h"
#include "ElementId.h"
#include "ISelection.h"
#include "IModelView.h"
#include "IUiDocument.h"
#include "Coordinate3d.h"
#include "IGraphicsText.h"
#include "Vector3dUtils.h"
#include "GbmpFileSystem.h"
#include "IGraphicsStyle.h"
#include "IAnnotationText.h"
#include "GcmpCommandState.h"
#include "IUserTransaction.h"
#include "IGraphicsCurve3d.h"
#include "IGraphicsBRepBody.h"
#include "IGraphicsBRepEdge.h"
#include "IGraphicsBRepFace.h"
#include "IGraphicsZoomFree.h"
#include "GraphicsNodeUtils.h"
#include "IElementModelShape.h"
#include "IGraphicsNodeGroup.h"
#include "IGraphicsStyleData.h"
#include "IPureGraphicsElement.h"
#include "IGraphicsElementShape.h"
#include "IUiDocumentViewManager.h"
#include "GraphicsNodeIdHistoryUtils.h"
#include "IGraphicsElementShapeReference.h"
#include "IElementViewSpecificShapeComponent.h"

#include "GRepCommandIds.h"
#include "CommandRegister.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h" 

using namespace gcmp;
using namespace Sample;

REGISTER_COMMAND(CmdShowGNodeId)

CmdShowGNodeId::CmdShowGNodeId()
    :CommandBase(ID_CMD_GREP_SHOW_GNODE_ID)
{
}

bool CmdShowGNodeId::IsEnabled() const
{
    return GcmpCommandState::IsInDocumentEnvironment();
}

bool CmdShowGNodeId::IsVisible() const
{
    return GcmpCommandState::IsInDocumentEnvironment();
}

// 创建显示样式，除了颜色都用默认设置
IGraphicsStyle* CreateGStyle(IDocument* pDoc, Color color)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDoc, L"pDoc为空",L"GDMP",L"2024-03-30");
    IGraphicsStyle* pGStyle = IGraphicsStyle::Create(pDoc, ElementCreationOptions::Transient);
    OwnerPtr<IGraphicsStyleData> opGStyleData = IGraphicsStyleData::Create();
    opGStyleData->Reset();
    opGStyleData->SetColor(color);
    opGStyleData->GetDataValiditySettings()->ValidateAll();
    pGStyle->SetGraphicsStyleData(*opGStyleData);
    return pGStyle;
}

void CmdShowGNodeId::DrawGNodeIdText(IElement* pElem, Vector3d position, GraphicsNodeId gNodeId, IGraphicsGroupBase* pGGroupToShowText, Color color)
{
    IDocument* pDoc = pElem->GetDocument();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc为空",L"GDMP",L"2024-03-30");
    IGraphicsStyle* pGStyle = nullptr;
    // 按照颜色缓存共享显示样式
    auto it = m_colorGStyleMap.find(color);
    if (it == m_colorGStyleMap.end())
    {
        pGStyle = CreateGStyle(pDoc, color);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pGStyle, L"pGStyle为空",L"GDMP",L"2024-03-30");
        m_colorGStyleMap[color] = pGStyle;
    }
    else
    {
        pGStyle = it->second;
    }

    // 避免文字叠在一起
    for (auto it : m_positions)
    {
        if (Vector3dUtils::Distance(position, it) < 10)
        {
            position.SetZ(position.Z() + 10);
        }
    }
    m_positions.emplace_back(position);

    GraphicsNodeId rootGNodeId = GraphicsNodeId::InvalidId;
    // 追溯节点标识历史，例如布尔操作后一个面变为两个，则拿到原面的节点标识并显示
    GraphicsNodeIdHistoryUtils::GetRootGraphicsNodeId(pElem, gNodeId, rootGNodeId);
    std::wstring idText = StringUtil::ToWString(gNodeId.AsInt());
    if (rootGNodeId.IsValid() && gNodeId != rootGNodeId)
    {
        idText += L"#" + StringUtil::ToWString(rootGNodeId.AsInt());
    }
    // 用IGraphicsZoomFree显示节点标识文字
    std::wstring fontPath = FileSystem::GetExeDirPath() + L"FZLTHJW.TTF";
    OwnerPtr<IGraphicsText> opGText = IGraphicsText::Create(fontPath, Vector3d::Zero, Vector3d::UnitX, Vector3d::UnitZ, 8.0, idText);
    DBG_WARN_AND_RETURN_VOID_UNLESS(opGText, L"opGText为空",L"GDMP",L"2024-03-30");
    OwnerPtr<IGraphicsZoomFree> opGZoomFree = IGraphicsZoomFree::Create();
    DBG_WARN_AND_RETURN_VOID_UNLESS(opGZoomFree, L"opGZoomFree为空",L"GDMP",L"2024-03-30");
    opGZoomFree->SetGraphicsStyleId(pGStyle->GetElementId());
    opGZoomFree->AddChild(TransferOwnership(opGText));
    Coordinate3d coord = Coordinate3d(position, Vector3d::UnitZ);
    opGZoomFree->SetCoordinate(coord);
    pGGroupToShowText->AddChild(TransferOwnership(opGZoomFree));
}

void CmdShowGNodeId::ShowGGroupId(IElement* pElem, const IGraphicsGroupBase* pGNodeGroup, const gcmp::Matrix4d& trf, IGraphicsGroupBase* pGGroupToShowText)
{
    IDocument* pDoc = pElem->GetDocument();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc为空",L"GDMP",L"2024-03-30");
    int count = pGNodeGroup->GetChildrenCount();
    for (int i = 0; i < count; i++)
    {
        const IGraphicsNode* pGNode = pGNodeGroup->GetChild(i);
        DBG_WARN_AND_CONTINUE_UNLESS(pGNode, L"pGNode为空",L"GDMP",L"2024-03-30");
        ShowGNodeId(pElem, pGNode, trf, pGGroupToShowText);
    }
    // 在包围盒中心位置显示GNodeId
    Box3d box = GraphicsNodeUtils::CalculateGraphicsNodeBoundingBox(pDoc, pGNodeGroup);
    Vector3d position = Matrix4dUtils::PointMultiplyMatrix(box.GetCenter(), trf);
    DrawGNodeIdText(pElem, position, pGNodeGroup->GetId(), pGGroupToShowText, Color::Red);
}

void CmdShowGNodeId::ShowGNodeId(IElement* pElem, const IGraphicsNode* pGNode, const gcmp::Matrix4d& trf, IGraphicsGroupBase* pGGroupToShowText)
{
    const IGraphicsNodeGroup* pGNodeGroup = quick_cast<IGraphicsNodeGroup>(pGNode);
    // 如果是IGraphicsNodeGroup
    if (pGNodeGroup)
    {
        ShowGGroupId(pElem, pGNodeGroup, trf, pGGroupToShowText);
        return;
    }
    const IGraphicsBRepBody* pGBrepBody = quick_cast<IGraphicsBRepBody>(pGNode);
    IDocument* pDoc = pElem->GetDocument();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc为空",L"GDMP",L"2024-03-30");
    // 如果是IGraphicsBRepBody
    if (pGBrepBody)
    {
        int edgeCount = pGBrepBody->GetEdgeCount();
        for (int i = 0; i < edgeCount; i++)
        {
            const IGraphicsBRepEdge* pGBrepEdge = pGBrepBody->GetEdge(i);
            const OwnerPtr<IGraphicsCurve3d> opCurve = pGBrepEdge->GetGeometryCurve();
            {
                Vector3d position = opCurve->GetMiddlePoint();
                DrawGNodeIdText(pElem, Matrix4dUtils::PointMultiplyMatrix(position, trf), pGBrepEdge->GetId(), pGGroupToShowText, Color::OliveDrab);
            }
        }
        int faceCount = pGBrepBody->GetFaceCount();
        for (int i = 0; i < faceCount; i++)
        {
            const IGraphicsBRepFace* pGBrepFace = pGBrepBody->GetFace(i);
            double midU = pGBrepFace->GetSurfaceRangeU().Middle();
            double midV = pGBrepFace->GetSurfaceRangeV().Middle();
            Vector3d position = pGBrepFace->GetPoint(midU, midV);
            DrawGNodeIdText(pElem, Matrix4dUtils::PointMultiplyMatrix(position, trf), pGBrepFace->GetId(), pGGroupToShowText, Color::Blue);
        }
        Box3d box = pGBrepBody->GetBox();
        DrawGNodeIdText(pElem, Matrix4dUtils::PointMultiplyMatrix(box.GetCenter(), trf), pGBrepBody->GetId(), pGGroupToShowText, Color::Purple);
        return;
    }
    const IGraphicsElementShapeReference* pGRepReference = quick_cast<IGraphicsElementShapeReference>(pGNode);
    // 如果是IGraphicsElementShapeReference
    if (pGRepReference)
    {
        ElementId refId = pGRepReference->GetReferencedElementId();
        IElement* pRefElem = pDoc->GetElement(refId);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pRefElem, L"pRefElem为空",L"GDMP",L"2024-03-30");
        IElementModelShape* pElementModelShape = pRefElem->GetElementModelShape();
        if (pElementModelShape)
        {
            Matrix4d refTrf = pGRepReference->GetTransformMatrix();
            Matrix4d newTrf = trf * refTrf;
            const IGraphicsElementShape* pRefGRep = pElementModelShape->GetGraphicsElementShape();
            if (pRefGRep)
            {
                ShowGGroupId(pRefElem, pRefGRep, newTrf, pGGroupToShowText);
            }
        }
    }
    // 其他的不显示
}

gcmp::OwnerPtr<gcmp::IAction> CmdShowGNodeId::ExecuteCommand(const gcmp::CommandParameters& cmdParams)
{
    IUiDocumentViewManager* pUiDocViewMgr = IUiDocumentViewManager::Get();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pUiDocViewMgr, L"获取UI文档视图管理器失败",L"GDMP",L"2024-03-30");
    IUiDocument* pUiDoc = pUiDocViewMgr->GetCurrentUiDocument();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pUiDoc, L"获取UI文档失败",L"GDMP",L"2024-03-30");
    IDocument* pDoc = pUiDoc->GetDbDocument();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDoc, L"获取文档失败",L"GDMP",L"2024-03-30");
    IUiView* pCurrentView = pUiDocViewMgr->GetCurrentUiView();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pCurrentView, L"pCurrentView为空",L"GDMP",L"2024-03-30");
    IModelView* pModelView = pCurrentView->GetModelView();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pModelView, L"pModelView为空",L"GDMP",L"2024-03-30");

    OwnerPtr<IUserTransaction> opTransaction = IUserTransaction::Create(pDoc, L"添加GNode");
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opTransaction, L"opTransaction为空",L"GDMP",L"2024-03-30");

    ISelection* pSelection = ISelection::Get();
    auto selections = pSelection->GetSelectedElements();
    // 遍历图元，创建文字用于显示节点标识
    for (ElementId id : selections)
    {
        IElement* pElem = pDoc->GetElement(id);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pElem, L"pElem为空", L"GDMP", L"2024-03-30");

        // 获得图元的视图GRep组件，对视图组件的GRep生成GNodeId的显示文字
        const IElementViewSpecificShapeComponent* pElemViewComponent = pElem->GetViewSpecificShapeComponent();
        if (nullptr != pElemViewComponent)
        {
            // 从视图GRep组件生成当前视图特有的GRep
            OwnerPtr<IGraphicsElementShape> opSourceGRep = pElemViewComponent->CreateViewSpecificElementShape(pModelView);
            if (nullptr != opSourceGRep)
            {
                OwnerPtr<IGraphicsNodeGroup> opGNodeGroup = IGraphicsNodeGroup::Create();
                Matrix4d trf;
                trf.MakeIdentity();
                ShowGGroupId(pElem, opSourceGRep.get(), trf, opGNodeGroup.get());

                OwnerPtr<IGraphicsElementShape> opGraphicsElementShape = IGraphicsElementShape::Create(GraphicsRenderLayer::Annotation);
                opGraphicsElementShape->AddChild(TransferOwnership(opGNodeGroup));
                IPureGraphicsElement* pPGEtoShowGNodeIds = IPureGraphicsElement::Create(pDoc, TransferOwnership(opGraphicsElementShape));
                // 如果有视图组件的GRep，就不继续处理模型图形表达了
                break;
            }
        }

        // 获得图元的模型图形表达，对模型GRep生成GNodeId的显示文字
        IElementModelShape* pElemModelShape = pElem->GetElementModelShape();
        if (pElemModelShape)
        {
            const IGraphicsElementShape* pGRep = pElemModelShape->GetGraphicsElementShape();
            if (pGRep)
            {
                OwnerPtr<IGraphicsNodeGroup> opGNodeGroup = IGraphicsNodeGroup::Create();
                Matrix4d trf;
                trf.MakeIdentity();
                ShowGGroupId(pElem, pGRep, trf, opGNodeGroup.get());

                OwnerPtr<IGraphicsElementShape> opGraphicsElementShape = IGraphicsElementShape::Create(GraphicsRenderLayer::Annotation);
                opGraphicsElementShape->AddChild(TransferOwnership(opGNodeGroup));
                IPureGraphicsElement* pPGEtoShowGNodeIds = IPureGraphicsElement::Create(pDoc, TransferOwnership(opGraphicsElementShape));
            }
        }
    }
    opTransaction->Commit();
    return nullptr;
}

